#ifndef WRAPIGRAPH_H_
#define WRAPIGRAPH_H_

#include <ext/hash_map>
namespace stdext
{
	using namespace __gnu_cxx;
}

#include <boost/shared_ptr.hpp>

#include "graph_support/Graph.h"
#include "graph_support/IGraph.h"

#include "graph_support/utils/PtrAccessOpSimulator.h"


namespace GRAPH_SUPPORT_NS
{
namespace impls
{
namespace wrap_igraph
{

template<
			typename AttributesT,
			typename HashFn = stdext::hash< size_t >,
			typename CmpFn = stdext::equal_to< size_t >
			> // TODO: unified test
// TODO: name Tmpl
class WrapIGraph : public Graph
{
private:
	typedef WrapIGraph< AttributesT, HashFn, CmpFn > MyT;

public:
	typedef size_t GrVertex;
	typedef AttributesT EdgeAttributes;
private:
	typedef typename IGraph::EdgeAttributesImpl< EdgeAttributes >::IAttributes IEdgeAttributes;
//	typedef typename IGraph::EdgeAttributesImpl< EdgeAttributes >::Attributes IEdgeAttributesImpl;
public:
	class GrEdge : public Graph::GrEdge
	{
	public:
		GrEdge( )
			: gr( NULL )
			, begVid( Graph::NOT_A_VERTEX_ID )
			, endVid( Graph::NOT_A_VERTEX_ID )
		{
		}
		GrEdge( MyT & gr, size_t begVid, IGraph::IEdgeIterator & iter )
			: gr( & gr )
			, begVid( begVid )
			, endVid( iter.getDestination() )
			, edgeAttributesSPtr( iter.getEdgeAttributes() )
		{
		}
	public:
		// TODO:
		const GrVertex beg() const;
		const GrVertex end() const;
		size_t begId() const
		{
			return begVid;
		}
		size_t endId() const
		{
			return endVid;
		}
	public:
		bool operator == ( const GrEdge & that ) const
		{
			return this->begVid == that.begVid && this->endVid == that.endVid;
		}
	public:
		EdgeAttributes & getAttributes()
		{
			IEdgeAttributes * edgeAttributesPtr = dynamic_cast< IEdgeAttributes * >( edgeAttributesSPtr.get() );
			return edgeAttributesPtr == NULL ?
					* ( ( EdgeAttributes * ) NULL ) :
					* edgeAttributesPtr->get();
		}
	private:
		MyT * gr;
		size_t begVid;
		size_t endVid;
		IGraph::IEdgeAttributesPtr edgeAttributesSPtr;
	};
	typedef utils::PtrAccessOpSimulator< GrEdge > GrEdgePtr;
	class VertexIterator /*: public Graph::VertexIterator*/
	{
	};
	class EdgeIterator : public Graph::EdgeIterator
	{
	public:
		EdgeIterator( MyT * gr, size_t vid, const IGraph::IEdgeIteratorPtr & iiter )
			: gr( gr )
			, vid( vid )
			, iiter( const_cast< IGraph::IEdgeIteratorPtr & >( iiter ).release() )
		{
		}
		static EdgeIterator getEndIterator()
		{
			IGraph::IEdgeIteratorPtr nullIter;
			return EdgeIterator( NULL, Graph::NOT_A_VERTEX_ID, nullIter );
		}
	public:
		const EdgeIterator & operator ++ ()
		{
			iiter->next();
			return * this;
		}
		bool operator != ( const EdgeIterator & that ) const
		{
			if ( that.iiter.get() == NULL )
			{
				return this->iiter.get() != NULL && this->iiter->notFinished();
			}
			return true;
		}
		GrEdgePtr operator * ()
		{
			return GrEdgePtr( GrEdge(
					* gr,
					vid,
					* iiter ) );
		}
		GrEdgePtr * operator -> ()
		{
			return NULL;
		}
	private:
		MyT * gr;
		size_t vid;
		boost::shared_ptr< IGraph::IEdgeIterator > iiter;
	};

public:
	typedef boost::shared_ptr< IGraph > IGraphPtr;

public:
	// TODO: check why is it here ?
	WrapIGraph( const IGraphPtr & grPtr )
		: grPtr( grPtr )
		, gr( * grPtr )
	{
	}

	WrapIGraph( IGraph & gr ) // TODO: write unit-test && doc
		: gr( gr )
	{
	}

	size_t getVertexCount() const
	{
		return gr.igetVertexCount();
	}

	VertexIterator getVerticesBeg();
	VertexIterator getVerticesEnd();

	EdgeIterator getEdgsBeg( const GrVertex & v );
	EdgeIterator getEdgsEnd( const GrVertex & v );
	EdgeIterator getEdgsBegId( size_t vid )
	{
		return EdgeIterator( this, vid, gr.igetOutEdgesIterator( vid ) );
	}

	EdgeIterator getEdgsEndId( size_t vid )
	{
		return EdgeIterator::getEndIterator();
	}

	size_t vert2num( const GrVertex & v ) const
	{
		return v;
	}

	GrVertex num2vert( size_t vid ) const
	{
		return vid;
	}

private:
	IGraphPtr grPtr;
	IGraph & gr;
};

}
}
}

#endif /* WRAPIGRAPH_H_ */
